package vip.liux.front.application.service.coupon;

import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import vip.liux.application.dots.ListResultDto;
import vip.liux.contracts.models.coupon.Coupon;
import vip.liux.contracts.models.coupon.UserCoupon;
import vip.liux.contracts.models.coupon.UserCouponDomainService;
import vip.liux.contracts.repositories.coupon.UserCouponRepository;
import vip.liux.contracts.shared.enums.coupon.UsedTag;
import vip.liux.core.exception.BusinessException;
import vip.liux.front.infrastructure.code.BizCodeGenerator;
import vip.liux.lock.LockTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserCouponService {
    private static final Logger log = LoggerFactory.getLogger(UserCouponService.class);

    private final UserCouponDomainService userCouponDomainService;
    private final UserCouponRepository userCouponRepository;
    private final CouponService couponService;
    private final LockTemplate lockTemplate;
    private final BizCodeGenerator codeGenerator;

    public UserCouponService(UserCouponDomainService userCouponDomainService, UserCouponRepository userCouponRepository, CouponService couponService, LockTemplate lockTemplate, BizCodeGenerator codeGenerator) {
        this.userCouponDomainService = userCouponDomainService;
        this.userCouponRepository = userCouponRepository;
        this.couponService = couponService;
        this.lockTemplate = lockTemplate;
        this.codeGenerator = codeGenerator;
    }

    /**
     * 查询可用优惠券并标记是否已领取
     *
     * @param userId 用户ID
     * @return 可用优惠券Dto列表
     */
    public ListResultDto<ShopCouponDto> queryAvailableCoupons(Long userId) {
        // 查询所有可用优惠券
        List<ShopCouponDto> coupons = couponService.queryAvailableCoupons().getItems();
        // 查询用户已领取的优惠券
        List<UserCoupon> userCoupons = userCouponRepository.findByUserId(userId);
        // 按优惠券编号分组统计已领取数量
        Map<String, Long> receivedCountMap = userCoupons.stream()
                .collect(Collectors.groupingBy(uc -> uc.getCoupon().getCouponNo(), Collectors.counting()));
        // 标记每个优惠券是否已达领取上限
        coupons.forEach(c -> {
            long receivedCount = receivedCountMap.getOrDefault(c.getCouponNo(), 0L);
            c.setReceived(NumberUtils.compare(c.getReceiveNumLimit(), (int) receivedCount) <= 0);
        });
        return new ListResultDto<>(coupons);
    }

    /**
     * 领取优惠券
     * 校验优惠券是否可领取，并通过分布式锁确保领取操作的原子性
     *
     * @param couponNo 优惠券编号
     * @param userId   用户ID
     */
    public void receiveCoupon(String couponNo, Long userId) {
        // 查询优惠券并校验领取条件
        Coupon coupon = userCouponDomainService.findCouponByNo(couponNo);
        userCouponDomainService.checkCoupon(coupon, userId);

        try {
            // 使用分布式锁执行领取操作，防止并发超发
            lockTemplate.execute("receive_coupon:" + couponNo, key -> {
                userCouponDomainService.checkCoupon(coupon, userId);
                String code = codeGenerator.generateUserCouponCode();
                UserCoupon userCoupon = userCouponDomainService.createUserCoupon(coupon, userId, code);
                return null;
            });
        } catch (Exception e) {
            log.error("领取优惠券失败", e);
            throw new BusinessException("领取优惠券失败");
        }
    }

    /**
     * 查询用户优惠券
     * 先自动更新过期状态，再根据用户ID和使用状态查询
     *
     * @param userId 用户ID
     * @param type   优惠券使用状态
     * @return 用户优惠券Dto列表
     */
    public List<UserCouponDto> query(Long userId, UsedTag type) {
        LocalDateTime now = LocalDateTime.now();
        // 更新过期优惠券状态
        int updated = userCouponRepository.updateExpiredCoupons(userId, UsedTag.UNUSED, now);
        if (updated > 0) {
            log.debug("更新过期优惠券成功, userId: {}, updatedCount: {}", userId, updated);
        }
        // 查询指定状态的优惠券
        List<UserCoupon> coupons = userCouponRepository.findByUserIdAndUsedTag(userId, type);
        return coupons.stream()
                .map(uc -> AutoMapperProfile.INSTANCE.toUserCoupon(uc, uc.getCoupon()))
                .toList();
    }
}
